/**
 * @class 地理系统绘制器
 * @author DuskyHuang 圣子
 * @description 提供坐标系统相关渲染逻辑。
*/

import { _decorator, Prefab, instantiate, Vec4, Node, Camera, Vec3, GeometryRenderer, Color, Mat3 } from 'cc';
import { Mediator, core } from 'cfw';
import { AxisRotation, I8 } from 'const';
import { GizmosProxy, gizmos, visit, planetary, cosmos } from 'proxies';
import { CelestialLabel, Coordinates, Orbiter } from 'prefabs';
import { CosmosController } from '../controllers/cosmos-controller';
import { setLabelStyle } from 'helpers';
import { View } from 'notifications';
const { ccclass, property } = _decorator;

@ccclass('cocosmos.geo-renderer')
export class GeoRenderer extends Mediator {

    @property(Prefab) public coordinates: Prefab = null;

    protected onLoad(): void {
        this._csc = core.getController(CosmosController);
        this.on(View.Transform.FACE_OVER, () => this._freeDiamLabel()._freeAxisLabel());
    }

    protected start(): void {
        const { center: c, deepSkyCamera: d, mainRenderer: r } = this._csc;
        this._celestial = this._createOne('天球坐标系', c, d, r).setKeyPrefix('cel').setDivision(DIV_BY_DEG)
            .setColors(CoordColor.CELESTIAL, EQUATORIAL).setRotation(AxisRotation.EQUATORIAL);
        this._ecliptic = this._createOne('黄道坐标系', c, d, r).setKeyPrefix('sun').setDivision(DIV_BY_DEG)
            .setColors(CoordColor.ECLIPTIC, ECLIPTIC).setRotation(AxisRotation.ECLIPTIC);
        this._galactic = this._createOne('银道坐标系', c, d, r).setKeyPrefix('mlk').setDivision(DIV_BY_DEG)
            .setColors(CoordColor.GALACTIC, GALACTIC).setRotation(AxisRotation.GALACTIC);
        this._horizontal = this._createOne('地平坐标系', c, d, r).setKeyPrefix('hor').setDivision(DIV_BY_DEG)
            .setColors(CoordColor.HORIZONTAL, HORIZONTAL).setRotation(Vec3.UNIT_Y);
    }

    protected update(dt: time.sec): void {
        this._drawCoordinates();
        this._drawLocalGeometries();
    }

    private _drawCoordinates(): void {
        const { showEquatorial, showHourAngle, showEcliptic, showGalactic, showHorizontal } = gizmos;
        if (this._celestial) {
            this._celestial.node.active = showEquatorial || showHourAngle;
            if (showEquatorial && !this._equatorial) {
                this._celestial.freeDegreesLabels();
                this._celestial.setDivision(DIV_BY_DEG);
                this._celestial.node.name = '天球坐标系';
                this._equatorial = true;
            } else if (showHourAngle && this._equatorial) {
                this._celestial.freeDegreesLabels();
                this._celestial.setDivision(DIV_BY_HA);
                this._celestial.node.name = '时角坐标系';
                this._equatorial = false;
            }
        }
        if (this._ecliptic) this._ecliptic.node.active = showEcliptic;
        if (this._galactic) this._galactic.node.active = showGalactic;
        if (this._horizontal) this._horizontal.node.active = showHorizontal;
    }

    private _drawLocalGeometries(): void {
        const { inPlanetary, inSatellite, center } = visit;
        if (!center || !(center instanceof Orbiter) || (!inPlanetary && !inSatellite)) {
            this._freeDiamLabel()._freeAxisLabel();
            return;
        }
        const { showAxis, showDiameter } = planetary;
        const renderer = this._csc.mainRenderer;
        const orbiter = center as Orbiter, { suggest } = orbiter;
        const alpha = Math.clamp01(suggest + 1.3 - cosmos.pow10);
        // Axis
        if (showAxis) {
            const { rotator: { worldMatrix: matrix } } = orbiter;
            color.set(LOCAL_AXIS);
            color.a = alpha * I8;
            Vec3.transformMat4(v3a, N_POLAR, matrix);
            Vec3.transformMat4(v3b, S_POLAR, matrix);
            renderer.addLine(v3a, v3b, color, true);
            Vec3.transformMat4(v3a, ARW_TIP, matrix);
            Vec3.transformMat4(v3b, ARW_A, matrix);
            renderer.addLine(v3a, v3b, color, true);
            Vec3.transformMat4(v3b, ARW_B, matrix);
            renderer.addLine(v3a, v3b, color, true);
            renderer.addArc(N_POLAR, 0.15, color, 0, 300, ARC, true, true, matrix);
            if (!this._lblAxis) {
                this._lblAxis = setLabelStyle(CelestialLabel.claim('planetary', '天体转轴'), 'planetary', 'axis');
                this._lblAxis.camera = this._csc.mainCamera;
                this._lblAxis.target = orbiter.node;
                const period = orbiter.config.orbiter.rotate.period;
                const day = isNum(period) ? period.toFixed(2) + 'day' : period;
                this._lblAxis.pure = '∠' + orbiter.rotateObliquity.toFixed(1) + '° ' + day;
                this._lblAxis.setOffset(Vec3.normalize(v3a, orbiter.rotator.up).multiplyScalar(0.75));
            }
            this._lblAxis.alpha = alpha;
        } else this._freeAxisLabel();
        // Diameter
        if (showDiameter) {
            const { rotator: { worldScale: { z } } } = orbiter;
            const r = z / 2;
            color.set(LOCAL_DIAM);
            color.a = alpha * I8;
            renderer.addLine(Vec3.set(v3a, 0, 0, -r), Vec3.set(v3b, r * 1.3, 0, -r), color, true);
            renderer.addLine(Vec3.set(v3a, 0, 0, r), Vec3.set(v3b, r * 1.3, 0, r), color, true);
            renderer.addLine(Vec3.set(v3a, r * 1.2, 0, -r), Vec3.set(v3b, r * 1.2, 0, r), color, true);
            renderer.addLine(v3a, v3c.set(r * 1.15, 0, -r * 0.9), color, true);
            renderer.addLine(v3a, v3c.add3f(r * 0.1, 0, 0), color, true);
            renderer.addLine(v3b, v3c.set(r * 1.15, 0, r * 0.9), color, true);
            renderer.addLine(v3b, v3c.add3f(r * 0.1, 0, 0), color, true);
            if (!this._lblDiam) {
                this._lblDiam = setLabelStyle(CelestialLabel.claim('planetary', '天体直径'), 'planetary', 'diameter');
                this._lblDiam.camera = this._csc.mainCamera;
                this._lblDiam.target = orbiter.node;
                this._lblDiam.string = (orbiter.config.diam * (orbiter.cat === 'satellite' ? 1e+6 : 1e+8)).toFixed(1) + ' km';
                this._lblDiam.setOffset(0.6, 0, 0);
            }
            this._lblDiam.alpha = alpha;
        } else this._freeDiamLabel();
    }

    private _freeDiamLabel(): this { this._lblDiam?.free(); this._lblDiam = null; return this; }
    private _freeAxisLabel(): this { this._lblAxis?.free(); this._lblAxis = null; return this; }

    private _createOne(name: string, center: Node, deepSkyCamera: Camera, renderer: GeometryRenderer): Coordinates {
        const created = instantiate(this.coordinates);
        this.node.addChild(created);
        created.name = name;
        return created.getComponent(Coordinates).setView(center, deepSkyCamera, renderer);
    }

    private _csc: CosmosController;
    private _equatorial: boolean = true;
    private _celestial: Coordinates;
    private _ecliptic: Coordinates;
    private _galactic: Coordinates;
    private _horizontal: Coordinates;

    private _lblAxis: CelestialLabel;
    private _lblDiam: CelestialLabel;
}

const { CoordColor, Segments: { ARC, CIRCLE },
    LineColor: { EQUATORIAL, ECLIPTIC, HORIZONTAL, GALACTIC, LOCAL_AXIS, LOCAL_DIAM } } = GizmosProxy;
const AXIS_LENGTH = 0.7;
const DIV_BY_HA = new Vec4(24, 18, 3, 2);
const DIV_BY_DEG = new Vec4(36, 18, 2, 2);
const N_POLAR = new Vec3(0, AXIS_LENGTH, 0);
const S_POLAR = new Vec3(0, -AXIS_LENGTH, 0);
const ARW_TIP = new Vec3(0.15, AXIS_LENGTH, 0);
const ARW_A = new Vec3(0.12, AXIS_LENGTH, 0.04);
const ARW_B = new Vec3(0.17, AXIS_LENGTH, 0.04);
const v3a = new Vec3;
const v3b = new Vec3;
const v3c = new Vec3;
const color = new Color;